home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / aijournl / 1986_09 / cli.lsp < prev    next >
Text File  |  1986-06-07  |  11KB  |  319 lines

  1.  
  2.  
  3. Multitasking Golden Common LISP program
  4.  
  5.  
  6. ;; initialization of parameters
  7. (setf *time-slice* 10)                ; quantum for switching
  8. (setf *beep-switch* t)                ; beep when switching
  9. (setf *random-seed* 10013)
  10. (setf *semaphore-list* nil)
  11. ;; The function which sets up the concurrent processes
  12. (defun cobegin (&rest forms)
  13.   ; initialize 
  14.   (setf *pseudo-time* 0                ; used to count pseudo-time
  15.     *switching?* t                ; inhibit switching if nil
  16.     *concur-length* (list-length forms))
  17.   ; create a list of the correct length for storing results
  18.   (setf stack-results-list (make-list *concur-length*))
  19.   ; create the stack groups
  20.   (make-stack-groups *concur-length*
  21.          (setf *stack-group-names*
  22.                (make-sym-list *concur-length*))
  23.          forms)
  24.   ; initiate task execution
  25.   (switch-around)
  26.   ; return the list of results
  27.   (mapcar 'eval stack-results-list)
  28. )
  29. ;;; The evaluator which handles concurrency
  30. (defun cli_eval (form)
  31.   ; increment the pseudo-time
  32.   (setf *pseudo-time* (1+ *pseudo-time*))
  33.   (cond    
  34.     ; is it time to switch?
  35.     ((and
  36.        ; is switching enabled?
  37.           *switching?*
  38.        ; don't switch if there's only 1 task
  39.        (> *concur-length* 1)
  40.        ; is it the end of a time quantum?
  41.           (>= *pseudo-time* *time-slice*)
  42.        ; don't want to leave the initial (gclisp) stack-group
  43.           (not (equal *current-stack-group*
  44.               *initial-stack-group*)))
  45.      ; if so,
  46.      ; beep if desired
  47.      (if *beep-switch* (beep))
  48.      ; reset pseudo-time
  49.      (setf *pseudo-time* 0)
  50.      ; suspend this task (and return to switch-around)
  51.      (stack-group-return nil)))
  52.   (let* 
  53.      ; evaluate this form
  54.     ((value    (evalhook form #'cli_eval nil))è         ; find the name of this stack-group
  55.      (name (assoc1 '*current-stack-group* *stack-group-names*)))
  56.     ; save the value if appropriate
  57.     (cond (name
  58.            (set (nth (get name 'process-num) stack-results-list) value)))
  59.     ; return the value of form
  60.     value)
  61. )
  62. ;; The scheduler for concurrent execution
  63. (defun switch-around ()
  64.   ; disable switching during the switching
  65.   (setf *switching?* nil)
  66.   (let
  67.        ; choose the next task
  68.        ((next (next-stack *concur-length* *stack-group-names*)))
  69.     (cond
  70.       ; if there are no more tasks, then we're done
  71.       ((null next)
  72.     (setf *switching?* t))
  73.       ; is the task finished?
  74.       ((< 1 (stack-group-status (eval next)))
  75.        ; if so,
  76.        ; eliminate this task
  77.        (setf *stack-group-names*
  78.          (remove next *stack-group-names* ))
  79.        (setf *concur-length* (1- *concur-length*))
  80.        ; make the memory reusable
  81.        (makunbound next)
  82.        ; try another task
  83.        (switch-around))
  84.       ; the task is ready to go
  85.       (t
  86.       (setf *switching?* t)
  87.          ; initiate it
  88.          (funcall (eval next) nil)
  89.          ; when its time-slice is done, we will return to here
  90.          ; and switch again
  91.          (switch-around))))
  92. )
  93. ;; HELP FUNCTIONS
  94. ;; this function returns the status of a stack group
  95. ;;      (0: active, 1:resumable, 2:broken, 3:exhausted)
  96. (defun stack-group-status (stack-group)
  97.   (multiple-value-setq
  98.     (offset segment) (%pointer stack-group))
  99.   (lsh (%contents segment (+ offset 41)) -1)
  100. )
  101. ;;  set up the stack-groups 
  102. (defun make-stack-groups (length name-list1 func-list)
  103.   (cond
  104.     ; done
  105.     ((null name-list1))
  106.     ; otherwise
  107.     (t 
  108.        ; create a stack group of the desired nameè       (set (car name-list1)
  109.         (stack-group-preset
  110.                     ; make the stack-group
  111.                     (make-stack-group (car name-list1)
  112.                               ; change as appropriate
  113.                               :regular-pdl-size 6000
  114.                               :special-pdl-size 2000)
  115.                     ; initialize to evaluate the form
  116.                     #'cli_eval (car func-list)))
  117.        ; recursive call to handle the next form
  118.        (make-stack-groups (1- length) (cdr name-list1) (cdr func-list))))
  119. )
  120. ;; create a list of names for stack-groups
  121. (defun make-sym-list (length &optional l)
  122.   (cond
  123.     ; are we done?
  124.     ((= 0 length) l)
  125.     ; nope
  126.     (t
  127.      (let 
  128.           ; create a name
  129.           ((name (gensym)))
  130.        ; give it a process identification number
  131.        (setf (get name 'process-num) (1- length))
  132.        ; recursive call to finish the rest
  133.        (make-sym-list (1- length) (cons name l)))))
  134. )
  135. ;; create a list of unique names with length n
  136. (defun make-list (n &optional l)
  137. (cond
  138.       ((= 0 n) l)
  139.       (t
  140.        (make-list (1- n) (cons (gensym) l))))
  141. )
  142. ;; selects next process to be executed
  143. (defun next-stack (length name-list)
  144.   ; choose the next process randomly
  145.   (nth
  146.        (rand 0 (1- length)) name-list)
  147. )
  148. ;; a random number generator (since Golden doesn't have one built-in)
  149. (defun rand (low-rand high-rand)
  150.   (setf
  151.     *random-seed*
  152.     (truncate (amod (* 25211.0 *random-seed*) 32768.0)))
  153.   (truncate
  154.         (+ low-rand (* (/ (float *random-seed*) 32768.0)
  155.                (1+ (- high-rand low-rand)))))
  156. )
  157. ;; define the mod function (since Golden's is in the editor!)
  158. (defun amod (real-num divisor)
  159.   (- real-num
  160.      (* (truncate (/ real-num divisor))
  161.     divisor))
  162. )è;; SEMAPHORE FUNCTIONS                                                     
  163. ;; handle the wait function
  164. (defun wait (which)
  165.   ; inhibit task switching
  166.   (setf *switching?* nil)
  167.   (cond 
  168.     ; if the semaphore is set at 1
  169.     ((eq (eval which) 1)
  170.      ; set it to 0 and retun
  171.      (set which 0)
  172.      (setf *switching?* t))
  173.     (t
  174.      ; else put this process on hold
  175.      (let 
  176.           ; find its name
  177.           ((process (assoc1 '*current-stack-group*
  178.                 *stack-group-names*)))
  179.        ; remove it from the ready processes
  180.        (setf *stack-group-names*
  181.          (remove process *stack-group-names*))
  182.        (setf *concur-length*
  183.          (1- *concur-length*))
  184.        ; add it to the queue waiting upon this semaphore
  185.        (setf (get which 'queue)
  186.          (cons process (get which 'queue)))
  187.        ; allow task switching
  188.        (setf *switching?* t)
  189.        ; leave this process (and switch to another)
  190.        (stack-group-return nil))))
  191. )
  192. ;; this function handles the SIGNAL operation.
  193. (defun signal (which)
  194.   ; inhibit task switching
  195.   (setf *switching?* nil)
  196.   (let 
  197.        ; get semaphore's queue
  198.        ((process (get which 'queue)))
  199.     (cond 
  200.       ; are there are tasks waiting upon this semaphore?
  201.       ((not (null process))
  202.        ; if so,
  203.        ; de-queue a task and add it to the ready tasks
  204.        (setf *stack-group-names*
  205.          (cons (car (last process)) *stack-group-names*))
  206.        (setf *concur-length*
  207.          (length *stack-group-names*))
  208.        ; remove the task from this semaphore's queue
  209.        (setf (get which 'queue) (butlast process)))
  210.       ; else set the semaphore to 1
  211.       (t (set which 1))))
  212.     ; enable task switching
  213.   (setf *switching?* t)
  214. )
  215. ;; initializes the semaphores
  216. ;; must be called before initiating concurrent taskingè(defun initialize-semaphores (sl)
  217.   (setf *semaphore-list* (i-s-help sl nil))
  218. )
  219. (defun i-s-help (sl l)
  220.   (cond ((null sl) l)
  221.         (t
  222.          (let ((which (caar sl))
  223.                (value (cadar sl)))
  224.            (set which value)
  225.            (setf (get which 'queue) nil)
  226.            (i-s-help (cdr sl) (cons which l)))))
  227. )
  228. ;; Find the name of a variable in the list given its unique value.
  229. (defun assoc1 (name list)
  230.   (cond ((null list) nil)
  231.     (t (cond ((equal (eval (car list)) (eval name))
  232.           (car list))
  233.          (t (assoc1 name (cdr list))))))
  234. )
  235. ;; EXAMPLES                                   
  236. ; producer-consumer (pc)
  237. ;; The Producer-Consumer Problem (synchronized)
  238. (defun pc ()
  239.   (setf buffer nil)
  240.   (setf information '(this is a test of semaphores))
  241.   ; initializes the semaphores
  242.   (initialize-semaphores '(($ok-to-consume 0) ($ok-to-produce 1)))
  243.   ; starts concurrent reading and writing.
  244.   (cobegin (list 'producer (length information))
  245.        (list 'consumer (length information)))
  246.   )
  247. (defun producer (r)
  248.   (do ((i 0 (1+ i)))
  249.       ((= i r) (print 'end-producer))
  250.     ; start of critical region
  251.     (wait '$ok-to-produce)
  252.     (print 'read-by-producer<---)
  253.     (setf buffer (nth i information))
  254.     (princ buffer)
  255.     (signal '$ok-to-consume)
  256.     ; end of critical region
  257.     )
  258. )
  259. (defun consumer (r)
  260.   (do ((i 0 (1+ i)))
  261.       ((= i r) (print 'end-consumer))
  262.     ; start of critical region
  263.     (wait '$ok-to-consume)
  264.     (print '----print-by-consumer--->)
  265.     (princ buffer)
  266.     (setf buffer nil)
  267.     (signal '$ok-to-produce)
  268.     ; end of critical region
  269.     )
  270. )è;; The Producer-Consumer Problem (unsynchronized)
  271. (defun un-pc ()
  272.   (setf buffer nil)
  273.   (setf information '(this is a test of semaphores))
  274.   ;; starts concurrent reading and writing.
  275.   (cobegin (list 'un-producer (length information))
  276.        (list 'un-consumer (length information)))
  277. )
  278. (defun un-producer (r)
  279.   (do ((i 0 (1+ i)))
  280.       ((= i r) (print 'end-producer))
  281.     (print 'read-by-producer<---)
  282.     (setf buffer (nth i information))
  283.     (princ buffer)
  284.     (terpri)
  285.     )
  286. )
  287. (defun un-consumer (r)
  288.   (do ((i 0 (1+ i)))
  289.       ((= i r) (print 'end-consumer))
  290.     (print '----print-by-consumer--->)
  291.     (princ buffer)
  292.     (terpri)
  293.     (setf buffer nil)
  294.     )
  295. )
  296. ;; A Note on Error Handling in CLI
  297. ;     The most common error is stack-group-overflow, i.e., running out of
  298. ; memory space.  Try reducing the size of each stack group (see function
  299. ╗ make-stack-groups)« Wheε aε erro≥ occur≤ withiε ß concurren⌠ ì
  300. ; task¼ tw∩ problem≤ result.
  301. ;     First, the GCLisp error handling routines were not designed to work
  302. ; with stack groups.  In particular, you cannot use Control-G to move up
  303. ; one listener level.  This is because the listeners use the catch-throw
  304. ; construct, and the catch is in the original stack group (the one which
  305. ; initiated concurrent execution) not the one which contains the error.
  306. ; You can use cntrl-C to return to the top-level of the original stack
  307. ; group, but then you are confronted with problem two.
  308. ;     When a stack group is exhausted, its name is unbound (in function
  309. ; switch-around) in order to reclaim the memory used.  However, if there
  310. ; is an error, this unbinding will be skipped.  Worse, GCLisp contains
  311. ; an apparent bug which does not allow reclamation of memory used by a
  312. ; stack group which terminates by being broken (i.e., with an error) 
  313. ; instead of by exhaustion.  Thus, any stack group which terminates in an
  314. ; error will continue to occupy (waste) memory.  The only solution to this
  315. ; problem is to exit GCLisp and restart.
  316. ;;  C. 1986 by Andrew P. Bernat.                                           
  317. ;;  Permission is granted for any noncommercial use with appropriate      
  318. ;;  credit to the author.                                                  
  319.